package com.xinsoft.service.impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinsoft.common.exception.BDException;
import com.xinsoft.entity.dto.*;
import com.xinsoft.entity.po.*;
import com.xinsoft.entity.vo.MesBomVo;
import com.xinsoft.mapper.MesBomDetailMapper;
import com.xinsoft.service.*;
import com.xinsoft.mapper.MesBomMapper;
import com.xinsoft.utils.ExcelUtils;
import com.xinsoft.utils.TokenUtil;
import com.xinsoft.validator.ValidatorUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author hsm
* @description 针对表【mes_bom(BOM主表)】的数据库操作Service实现
* @createDate 2023-07-18 14:07:11
*/
@Service
public class MesBomServiceImpl extends ServiceImpl<MesBomMapper, MesBom>
    implements MesBomService{

    @Resource
    private SysUserService sysUserService;
    @Resource
    private SysFileHisService sysFileHisService;
    @Resource
    private MesBomDetailMapper mesBomDetailMapper;
    @Resource
    private MesBomDetailService mesBomDetailService;
    @Resource
    private SysDictionaryService sysDictionaryService;
    @Resource
    private SysMaterialService sysMaterialService;
    @Resource
    private MesProcessService mesProcessService;
    @Resource
    private MesStationService mesStationService;
    @Resource
    private SysWarehouseService sysWarehouseService;
    @Resource
    private SysWarehouseItemService sysWarehouseItemService;

    /**
     * 分页
     * @param param
     * @return
     */
    @Override
    public IPage<MesBomVo> findPageListByParam(MesBomPageParam param) {
        //分页
        Page<MesBomVo> page = new Page<MesBomVo>(param.getCurrentPage(), param.getPageSize());

        IPage<MesBomVo> resultPage = this.baseMapper.findListByParam(page, param);
        //返回值
        return resultPage;
    }

    /**
     * 保存
     * @param mesBomDTO
     * @return
     */
    @Override
    public MesBom addMesBom(MesBomDTO mesBomDTO) {
        // 名称和编号 验重

        // 判断名称是否重复
        QueryWrapper<MesBom> nameQuery = new QueryWrapper<>();
        nameQuery.select("id");
        nameQuery.eq("bom_name",mesBomDTO.getBomName());
        List names = this.list(nameQuery);

        if(CollectionUtil.isNotEmpty(names)){
            throw new BDException("操作失败，Bom名称重复！");
        }

        //判断编号否重复
        QueryWrapper<MesBom> codeQuery = new QueryWrapper<>();
        codeQuery.select("id");
        codeQuery.eq("bom_no",mesBomDTO.getBomNo());
        List codes = this.list(codeQuery);
        if(CollectionUtil.isNotEmpty(codes)) {
            throw new BDException("操作失败，Bom编号重复！");
        }
        mesBomDTO.setAuditStatus(0);
        mesBomDTO.setAuditId(null);
        MesBom mesBom = new MesBom();
        BeanUtils.copyProperties(mesBomDTO,mesBom);
        save(mesBom);

        //保存detail
        saveDetail(mesBom.getId(),mesBomDTO.getBomTree(),0);

        return mesBom;
    }

    /**
     * 编辑
     * @param mesBomDTO
     * @return
     */
    @Override
    public MesBom editMesBom(MesBomDTO mesBomDTO) {
        QueryWrapper<MesBom> nameQuery = new QueryWrapper<>();
        nameQuery.select("id");
        nameQuery.eq("bom_name",mesBomDTO.getBomName());
        nameQuery.ne("id",mesBomDTO.getId());
        List names = this.list(nameQuery);

        if(CollectionUtil.isNotEmpty(names)){
            throw new BDException("操作失败，Bom名称重复！");
        }

        //判断编号否重复
        QueryWrapper<MesBom> codeQuery = new QueryWrapper<>();
        codeQuery.select("id");
        codeQuery.eq("bom_no",mesBomDTO.getBomNo());
        codeQuery.ne("id",mesBomDTO.getId());
        List codes = this.list(codeQuery);
        if(CollectionUtil.isNotEmpty(codes)) {
            throw new BDException("操作失败，Bom编号重复！");
        }

        MesBom entity = getById(mesBomDTO.getId());
        if(Objects.equals(entity.getAuditStatus(),1)){
            throw new BDException("操作失败，已审核无法编辑！");
        }

        mesBomDTO.setAuditStatus(0);
        mesBomDTO.setAuditId(null);
        MesBom mesBom = new MesBom();
        BeanUtils.copyProperties(mesBomDTO,mesBom);
        updateById(mesBom);
        mesBomDTO.setId(mesBom.getId());

        //删除detail
        QueryWrapper deleteQuery = new QueryWrapper<>();
        deleteQuery.eq("bom_id",mesBom.getId());
        mesBomDetailMapper.delete(deleteQuery);

        //保存detail
        saveDetail(mesBom.getId(),mesBomDTO.getBomTree(),0);

        return mesBom;
    }

    /**
     * 递归保存明细
     * @param bomId
     * @param bomTree
     * @param parentId
     */
    private void saveDetail(Integer bomId,BomTree bomTree,Integer parentId){

        if(bomTree!=null){
            MesBomDetail detail = new MesBomDetail();
            BeanUtils.copyProperties(bomTree,detail);
            detail.setBomId(bomId);
            detail.setParentBomId(parentId);
            //清除id,重新保存
            detail.setId(null);
            //保存
            mesBomDetailService.save(detail);

            List<BomTree> childrenList = bomTree.getChildren();;
            if(CollectionUtil.isNotEmpty(childrenList)){
                for (BomTree tree : childrenList) {
                    saveDetail(bomId,tree,detail.getId());
                }
            }else{
                return;
            }
        }
    }

    /**
     * 删除
     * @param ids
     */
    @Override
    public void del(List<Integer> ids) {

        QueryWrapper<MesBom> query = new QueryWrapper<>();
        query.eq("audit_status",1);//审核
        query.in("id",ids);//审核
        List<MesBom> list = this.list(query);
        //判断是否已启用
        if(CollectionUtil.isNotEmpty(list)){
            throw new BDException("操作失败，"+list.get(0).getBomName()+"已审核无法删除！");
        }
        //删除bom
        this.removeByIds(ids);

        //删除detail
        QueryWrapper deleteQuery = new QueryWrapper<>();
        deleteQuery.in("bom_id",ids);
        mesBomDetailMapper.delete(deleteQuery);
    }

    /**
     * 详情
     * @param id
     * @return
     */
    @Override
    public MesBomDTO getMesBom(Integer id) {
        MesBomDTO mesBomDTO = new MesBomDTO();
        MesBom mesBom = this.getById(id);
        BeanUtils.copyProperties(mesBom,mesBomDTO);
        //获取审核人
        SysUser user = sysUserService.getById(mesBom.getAuditId());
        if(user!= null){
            mesBomDTO.setAuditName(user.getRealName());
        }

        List<MesBomDetailDTO> detailDTOList =  mesBomDetailMapper.findListByParam(id,null);

        List<BomTree> trees = new ArrayList<BomTree>();

        for (MesBomDetailDTO mesBomDetailDTO : detailDTOList) {
            BomTree tree = new BomTree();

            BeanUtils.copyProperties(mesBomDetailDTO,tree);

            tree.setId(mesBomDetailDTO.getId().toString());
            tree.setParentId(ObjectUtil.isEmpty(mesBomDetailDTO.getParentBomId()) ? "0" : mesBomDetailDTO.getParentBomId().toString());
            tree.setText(mesBomDetailDTO.getMaterialName());
            trees.add(tree);
        }
        // 默认顶级菜单为０，根据数据库实际情况调整
        mesBomDTO.setBomTree(build(trees));

        return mesBomDTO;
    }

    /**
     * 审核
     * @param ids
     */
    @Override
    public void audit(List<Integer> ids) {
        UpdateWrapper<MesBom> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("audit_status",1);//审核
        updateWrapper.set("audit_id", TokenUtil.getUserId());//设置审核人
        updateWrapper.in("id",ids);
        updateWrapper.eq("do_delete",0);
        this.update(updateWrapper);
    }

    /**
     * 反审核
     * @param ids
     */
    @Override
    public void deAudit(List<Integer> ids) {
        UpdateWrapper<MesBom> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("audit_status",0);//审核
        updateWrapper.set("audit_id", TokenUtil.getUserId());//设置审核人
        updateWrapper.in("id",ids);
        updateWrapper.eq("do_delete",0);
        this.update(updateWrapper);
    }



    @Override
    public JSONObject importExcel(MultipartFile multipartFile, HttpServletResponse response) throws IOException {
        // 初始化错误行信息list，用来记录导入失败的rowNum
        List<ExcelErrRowDTO> errRowDOs = new ArrayList<>();

        // 对excel是否为空进行check
        if (multipartFile.isEmpty()) {
            throw new BDException("导入失败，请选择文件！");
        }

        //设置excel参数
        ImportParams params = new ImportParams();
        params.setTitleRows(1);
        params.setHeadRows(1);
        List<MesBomImportDTO> mesBomImportList;
        List<MesBomImportDTO> mesBomList = CollectionUtil.newArrayList();
        List<MesBomImportDTO> mesBomDetailList = CollectionUtil.newArrayList();

        JSONObject result = new JSONObject();

        // 解析excel中的行，mesBomImportList
        try {
            // 解析excel
            mesBomImportList = ExcelImportUtil.importExcel(multipartFile.getInputStream(), MesBomImportDTO.class, params);
        } catch (Exception e) {
            throw new BDException("导入失败，文件内容解析错误！");
        }

        //取出excel和db中所有重复的name
        List<String> excelNames = mesBomImportList.stream()
                .filter(x-> x.getNum() ==0)
                .map(MesBomImportDTO::getBomName)
                .collect(Collectors.toList()); // 获取excel中所有的username
        List<String> dbNames = this.getAllName();//获取系统中所有的name
        List<String> duplicateNames = new ArrayList<>(); // 存放存在重复的name，用于定位重复存放存在重复的name的行

        if (dbNames.size() > 0 && excelNames.size() > 0) {
            dbNames.addAll(excelNames);//合并
            duplicateNames = ExcelUtils.getDuplicateElements(dbNames);//取出存在重复的name
        }

        //取出excel和db中所有重复的code
        List<String> excelCodes = mesBomImportList.stream()
                .filter(x-> x.getNum() ==0)
                .map(MesBomImportDTO::getBomNo)
                .collect(Collectors.toList()); // 获取excel中所有的code
        List<String> dbCodes = this.getAllCode();//获取系统中所有的code
        List<String> duplicateCodes = new ArrayList<>(); // 存放存在重复的code，用于定位重复存放存在重复的code的行

        if (dbCodes.size() > 0 && excelCodes.size() > 0) {
            dbCodes.addAll(excelCodes);//合并
            duplicateCodes = ExcelUtils.getDuplicateElements(dbCodes);//取出存在重复的code
        }

        //取出excel所有重复的序号
        List<Integer> numList = mesBomImportList.stream()
                .map(MesBomImportDTO::getNum).filter(num-> num != null)
                .collect(Collectors.toList()); // 获取excel中所有的code
        List<String> duplicateNum = new ArrayList<>();
        duplicateNum = ExcelUtils.getDuplicateElements(duplicateNum);//取出存在重复的序号

        //序号-bom名称
        Map<Integer, String> bomNumMap = mesBomImportList.stream()
                .collect(Collectors.toMap(MesBomImportDTO::getNum, MesBomImportDTO::getBomName, (v1, v2) -> v2));

        // BOM类别
        List<SysDictionary> bomTypeList =  sysDictionaryService.getDictByTypeCode("bom_type");
        Map<String, Integer> bomTypeMap = bomTypeList.stream()
                .collect(Collectors.toMap(SysDictionary::getDicName, SysDictionary::getId, (v1, v2) -> v2));

        // 产品
        List<SysMaterial> materialList =  sysMaterialService.list();
        Map<String, Integer> materialMap = materialList.stream()
                .collect(Collectors.toMap(SysMaterial::getMaterialName, SysMaterial::getId, (v1, v2) -> v2));

        // 工序
        List<MesProcess> processList =  mesProcessService.list();
        Map<String, Integer> processMap = processList.stream()
                .collect(Collectors.toMap(MesProcess::getProcessName, MesProcess::getId, (v1, v2) -> v2));

        // 工位
        List<MesStation> stationList =  mesStationService.list();
        Map<String, Integer> stationMap = stationList.stream()
                .collect(Collectors.toMap(MesStation::getStationName, MesStation::getId, (v1, v2) -> v2));

        // 仓库
        List<SysWarehouse> warehouseList =  sysWarehouseService.list();
        Map<String, Integer> warehouseMap = warehouseList.stream()
                .collect(Collectors.toMap(SysWarehouse::getWarehouseName, SysWarehouse::getId, (v1, v2) -> v2));

        // 库位
        List<SysWarehouseItem> warehouseItemList =  sysWarehouseItemService.list();
        Map<String, Integer> warehouseItemMap = warehouseItemList.stream()
                .collect(Collectors.toMap(SysWarehouseItem::getWhItemName, SysWarehouseItem::getId, (v1, v2) -> v2));


        //校验
        if (mesBomImportList.size() > 0) {

            int index = 1;

            for (MesBomImportDTO mesBomImportDTO : mesBomImportList) {
                index ++;
                // check
                StringBuilder stringBuilder = new StringBuilder();
                ExcelErrRowDTO excelErrRowDO= new ExcelErrRowDTO();

                // 参数校验
                stringBuilder.append(ValidatorUtils.validateExcel(mesBomImportDTO));

                // 检验Name 是否重复
                if(duplicateNames.contains(mesBomImportDTO.getBomName())){
                    stringBuilder.append("名称已经存在，请勿重复！");
                }
                // 检验Code 是否重复
                if(duplicateCodes.contains(mesBomImportDTO.getBomNo())){
                    stringBuilder.append("编号已经存在，请勿重复！");
                }
                // 检验序号 是否重复
                if(duplicateNum.contains(mesBomImportDTO.getNum())){
                    stringBuilder.append("序号已经存在，请勿重复！");
                }
                // 检验序号 与父序号 的顺序
                if(mesBomImportDTO.getNum()!=null && mesBomImportDTO.getNum().compareTo(mesBomImportDTO.getParentNum())<=0){
                    stringBuilder.append("序号必须大于父序号！");
                }
                // 检验父序号是否在同一bom中
                if(mesBomImportDTO.getParentNum()!=0 && !StringUtils.equals(mesBomImportDTO.getBomName(),bomNumMap.get(mesBomImportDTO.getParentNum()))){
                    stringBuilder.append("与父序号 不在同一bom内！");
                }

                // BOM类别
                if(StringUtils.isNotEmpty(mesBomImportDTO.getBomTypeName())){
                    Integer typeId = bomTypeMap.get(mesBomImportDTO.getBomTypeName());
                    if(typeId==null){
                        stringBuilder.append("BOM类别:"+mesBomImportDTO.getBomTypeName()+",在系统中不存在！");
                    }
                    mesBomImportDTO.setBomType(typeId);
                }

                // 产品
                if(StringUtils.isNotEmpty(mesBomImportDTO.getMaterialName())){
                    Integer material = materialMap.get(mesBomImportDTO.getMaterialName());
                    if(material==null){
                        stringBuilder.append("产品:"+mesBomImportDTO.getMaterialName()+",在系统中不存在！");
                    }
                    mesBomImportDTO.setMaterialId(material);
                }

                // 工序号
                if(StringUtils.isNotEmpty(mesBomImportDTO.getProcessName())){
                    Integer processId = processMap.get(mesBomImportDTO.getProcessName());
                    if(processId==null){
                        stringBuilder.append("工序:"+mesBomImportDTO.getProcessName()+",在系统中不存在！");
                    }
                    mesBomImportDTO.setProcessId(processId);
                }

                // 工位
                if(StringUtils.isNotEmpty(mesBomImportDTO.getStationName())){
                    Integer stationId = stationMap.get(mesBomImportDTO.getStationName());
                    if(stationId==null){
                        stringBuilder.append("工位:"+mesBomImportDTO.getStationName()+",在系统中不存在！");
                    }
                    mesBomImportDTO.setProcessId(stationId);
                }

                // 仓库
                if(StringUtils.isNotEmpty(mesBomImportDTO.getWarehouseName())){
                    Integer  warehouseId = warehouseMap.get(mesBomImportDTO.getWarehouseName());
                    if(warehouseId==null){
                        stringBuilder.append("仓库:"+mesBomImportDTO.getWarehouseName()+",在系统中不存在！");
                    }
                    mesBomImportDTO.setWarehouseId(warehouseId);
                }

                // 仓位
                if(StringUtils.isNotEmpty(mesBomImportDTO.getWarehItemName())){
                    Integer  warehouseItemId = warehouseItemMap.get(mesBomImportDTO.getWarehItemName());
                    if(warehouseItemId==null){
                        stringBuilder.append("仓位:"+mesBomImportDTO.getWarehItemName()+",在系统中不存在！");
                    }
                    mesBomImportDTO.setWarehItemId(warehouseItemId);
                }

                if(Objects.equals(mesBomImportDTO.getParentNum(),0)){
                    mesBomList.add(mesBomImportDTO);
                }
                mesBomDetailList.add(mesBomImportDTO);


                // 将错误行信息保存到list
                String errMsg = stringBuilder.toString();
                if(StringUtils.isNotEmpty(errMsg)){
                    excelErrRowDO.setRowIndex(index);
                    excelErrRowDO.setErrMessage(errMsg);
                    errRowDOs.add(excelErrRowDO);
                    continue;
                }
            }

            if(errRowDOs.size()==0){
                HashMap<String,Integer> bomMap = new HashMap();
                HashMap<String,Integer> bomDetailMap = new HashMap();
                MesBom bom = new MesBom();
                MesBomDetail bomDetail = new MesBomDetail();
                //保存bom
                for (MesBomImportDTO mesBomImportDTO : mesBomList) {
                    bom = new MesBom();
                    BeanUtils.copyProperties(mesBomImportDTO,bom);
                    save(bom);
                    bomMap.put(mesBomImportDTO.getBomName(),bom.getId());
                }

                //保存bomDetail
                for (MesBomImportDTO mesBomImportDTO : mesBomDetailList) {
                    bomDetail = new MesBomDetail();
                    BeanUtils.copyProperties(mesBomImportDTO,bomDetail);
                    bomDetail.setBomId(bomMap.get(mesBomImportDTO.getBomName()));
                    mesBomDetailService.save(bomDetail);
                    mesBomImportDTO.setDetailId(bomDetail.getId());
                    bomDetailMap.put(mesBomImportDTO.getNum().toString(),bomDetail.getId());
                }
                //更新parentId
                for (MesBomImportDTO mesBomImportDTO : mesBomDetailList) {
                    bomDetail = new MesBomDetail();
                    bomDetail.setId(mesBomImportDTO.getDetailId());
                    bomDetail.setParentBomId(bomDetailMap.get(mesBomImportDTO.getNum()));
                    mesBomDetailService.updateById(bomDetail);
                    bomMap.put(mesBomImportDTO.getNum().toString(),bomDetail.getId());
                }
            }
            // 导出错误行
            String errFileUrl = ExcelUtils.exportErrRows(errRowDOs,multipartFile,sysFileHisService);
            result.put("errFileUrl",errFileUrl);
        }
        return result;
    }

    @Override
    public void insertOrUpdateBatch(List<MesBom> mesBomList) {
        baseMapper.insertOrUpdateBatch(mesBomList);
    }

    private List<String> getAllCode() {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.select("bom_code");
        List<MesBom> userList = this.list();
        List<String> codes = userList.stream().map(MesBom::getBomNo).collect(Collectors.toList());
        return codes;
    }

    private List<String> getAllName() {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.select("bom_name");
        List<MesBom> userList = this.list();
        List<String> names = userList.stream().map(MesBom::getBomName).collect(Collectors.toList());
        return names;
    }


    /**
     * 构建树
     * @param nodes
     * @return
     * @param nodes
     */
    private BomTree build(List<BomTree> nodes) {

        if (nodes == null) {
            return null;
        }
        List<BomTree> topNodes = new ArrayList<BomTree>();

        for (BomTree children : nodes) {

            String pid = children.getParentId();
            if (pid == null || "0".equals(pid)) {
                topNodes.add(children);

                continue;
            }

            for (BomTree parent : nodes) {
                String id = parent.getId();
                if (id != null && id.equals(pid)) {
                    parent.getChildren().add(children);
                    continue;
                }
            }

        }

        return topNodes.get(0);
    }
}




